import java.util.*;
import java.util.function.*;
import java.io.*;
import java.nio.file.*;
import java.time.Instant;

public class Main {
    static List<String> ops = new ArrayList<>();
    static int on = 0;
    static String JANpost;
    static Map<String, Short> fcl = new HashMap<>();
    static long ct;
    static int ling = 0;
    static JanusValue ret = new JanusValue();
    static Map<String, Function<String, JanusValue>> tokens = new HashMap<>();
    static Map<String, JanusValue> vmap = new HashMap<>();
    static int janus_seed = (int) Instant.now().toEpochMilli();

    enum Type { S, I, C, B, P, F, O, V, E } // 字符串, 整数, 字符, 布尔, 指针, 函数, 文件, void, 错误

    static class JanusValue {
        String str;
        int _int;
        char _char;
        boolean _bool;
        Type ty;
        RandomAccessFile file;
        int funcLine;

        JanusValue() { ty = Type.V; }
        JanusValue(Type type) { ty = type; }

        @Override
        public String toString() {
            switch (ty) {
                case S: return str;
                case I: return Integer.toString(_int);
                case C: return Character.toString(_char);
                case B: return Boolean.toString(_bool);
                case P: return "[ptr]" + str;
                case F: return "Function@" + funcLine;
                case O: return "File:" + str;
                default: return "()";
            }
        }
    }

    public static void main(String[] args) {
        JANpost = args.length > 0 ? args[0] : "";
        Path exePath = Paths.get(JANpost);
        String exeDir = exePath.getParent() != null ? exePath.getParent().toString() + File.separator : "";
        String dirf = exeDir + "lib" + File.separator;

        initTokens();
        Scanner consoleIn = new Scanner(System.in);
        System.out.print("IJanus[版本0.1.1]");
        boolean debug = false;

        while (true) {
            System.out.print("\n>\n");
            ops.clear();
            on = 0;
            ling = 0;

            while (true) {
                String line = consoleIn.nextLine();
                if (line.equals("#end")) break;
                if (line.equals("#clear")) {
                    vmap.clear();
                    ops.clear();
                    on = 0;
                    continue;
                }
                if (line.equals("#exit")) System.exit(0);
                if (line.equals("#show")) { debug = true; continue; }
                if (line.equals("#hide")) { debug = false; continue; }

                String[] parts = line.split("\\s+", 2);
                if (parts.length > 0) {
                    if (parts[0].equals("func") && parts.length > 1) {
                        String[] funcParts = parts[1].split("\\s+");
                        if (funcParts.length > 1) {
                            JanusValue v = new JanusValue(Type.F);
                            v.funcLine = ops.size();
                            v.str = funcParts[1];
                            vmap.put(funcParts[0], v);
                        }
                    } else if (parts[0].equals("Import") && parts.length > 1) {
                        readFile(parts[1]);
                        continue;
                    } else if (parts[0].equals("tag") && parts.length > 1) {
                        JanusValue v = new JanusValue(Type.F);
                        v.funcLine = ops.size();
                        vmap.put(parts[1], v);
                    } else if (parts[0].equals("import") && parts.length > 1) {
                        readFile(dirf + parts[1]);
                        continue;
                    }
                }
                ops.add(line);
            }

            cut();
            long startTime = System.currentTimeMillis();
            JanusValue result = explain();
            long duration = System.currentTimeMillis() - startTime;

            if (debug) {
                System.out.println("\n--------------------------------");
                System.out.printf("Process exited after %d.%d seconds with return value ",
                        duration / 1000, duration % 1000);
                switch (result.ty) {
                    case S: System.out.print('"' + result.str + '"'); break;
                    case I: System.out.print(result._int); break;
                    case C: System.out.print('\'' + result._char + '\''); break;
                    case B: System.out.print(result._bool); break;
                    case V: System.out.print("()"); break;
                    default: System.out.print(result);
                }
                System.out.println();
            }
        }
    }

    static void readFile(String filename) {
        try (BufferedReader br = new BufferedReader(new FileReader(filename))) {
            String line;
            while ((line = br.readLine()) != null) {
                ops.add(line);
            }
        } catch (IOException e) {
            System.err.println("Error reading file: " + filename);
        }
    }

    static void cut() {
        for (int i = ling; i < ops.size(); i++) {
            String currentOp = ops.get(i);
            if (currentOp.isEmpty() || currentOp.startsWith("Debug")) continue;

            StringBuilder newOp = new StringBuilder();
            boolean inString = false;
            char quote = 0;
            char lastType = 0;

            for (int j = 0; j < currentOp.length(); j++) {
                char ch = currentOp.charAt(j);
                if (!inString && (ch == '\"' || ch == '\'')) {
                    inString = true;
                    quote = ch;
                    newOp.append(ch);
                    lastType = 'e';
                    continue;
                }
                if (inString && ch == quote && (j == 0 || currentOp.charAt(j - 1) != '\\')) {
                    inString = false;
                    quote = 0;
                }

                if (!inString) {
                    char ct = charClass(ch);
                    if (ct != lastType && j != 0) {
                        newOp.append(' ');
                        lastType = ct;
                    }
                    if (j == 0) lastType = ct;
                }
                newOp.append(ch);
            }
            ops.set(i, newOp.toString());
        }
    }

    static char charClass(char ch) {
        if (Character.isLetterOrDigit(ch)) return 'a';
        if (ch < 32 || ch == '[' || ch == '_' || ch == '$') return 'a';
        if (ch == '#' || ch == '{') return '#';
        return 'e';
    }

    static String parseEscape(String s) {
        StringBuilder res = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == '\\' && i + 1 < s.length()) {
                res.append(shift(s.charAt(i + 1)));
                i++;
            } else {
                res.append(s.charAt(i));
            }
        }
        return res.toString();
    }

    static char shift(char ch) {
        switch (ch) {
            case 'a': return '\u0007'; // bell
            case 'b': return '\b';
            case 'f': return '\f';
            case 'n': return '\n';
            case 'r': return '\r';
            case 't': return '\t';
            case 'v': return '\u000B'; // vertical tab
            case '\\': return '\\';
            case '\'': return '\'';
            case '"': return '"';
            case 'e': return '\u001B'; // escape
            case 's': return ' ';
            default: return ch;
        }
    }

    static JanusValue explain() {
        JanusValue t = new JanusValue(Type.V);

        try {
            for (; ling < ops.size(); ling++) {
                String line = ops.get(ling);
                if (line.isEmpty()) continue;
                pexpl(new StringBuilder(line));
            }
        } catch (ReturnException e) {
            ling++;
            return ret;
        }
        return t;
    }

    static class ReturnException extends RuntimeException {}

    static JanusValue pexpl(StringBuilder line) {
        if (line.length() == 0) return new JanusValue();

        // 跳过前导空格
        while (line.length() > 0 && line.charAt(0) == ' ') {
            line.deleteCharAt(0);
        }
        if (line.length() == 0) return new JanusValue();

        // 提取第一个token
        int end = line.indexOf(" ");
        String sop;
        if (end == -1) {
            sop = line.toString();
            line.setLength(0);
        } else {
            sop = line.substring(0, end);
            line.delete(0, end + 1);
        }

        if (sop.isEmpty()) return new JanusValue();

        if (sop.charAt(0) == '#') {
            return new JanusValue();
        } else if (sop.charAt(0) == '\'') {
            JanusValue t = new JanusValue(Type.C);
            String content = parseEscape(sop.substring(1, sop.length() - 1));
            t._char = content.isEmpty() ? 0 : content.charAt(0);
            return t;
        } else if (sop.charAt(0) == '"') {
            JanusValue t = new JanusValue(Type.S);
            t.str = parseEscape(sop.substring(1, sop.length() - 1));
            return t;
        } else if (Character.isDigit(sop.charAt(0))) {
            JanusValue t = new JanusValue(Type.I);
            try {
                t._int = Integer.parseInt(sop);
            } catch (NumberFormatException e) {
                t._int = 0;
            }
            return t;
        } else {
            // 函数调用处理
            if (tokens.containsKey(sop)) {
                return tokens.get(sop).apply(line.toString());
            }

            // 处理指针解引用
            while (sop.startsWith("[")) {
                sop = sop.substring(1);
                StringBuilder tempLine = new StringBuilder(sop);
                JanusValue t = pexpl(tempLine);
                if (t.ty == Type.S) sop = t.str;
            }

            if (!vmap.containsKey(sop)) {
                System.err.println("Undefined token: " + sop);
                System.err.println("That line: " + ops.get(ling));
                return new JanusValue();
            }

            JanusValue v = vmap.get(sop);
            if (v.ty == Type.F) {
                int savedLing = ling;
                ling = v.funcLine;

                // 创建参数
                String args = line.toString();
                try {
                    int argCount = Integer.parseInt(v.str);
                    for (int i = 1; i <= argCount; i++) {
                        StringBuilder argLine = new StringBuilder(args);
                        JanusValue argVal = pexpl(argLine);
                        vmap.put("var" + i, argVal);
                        args = argLine.toString().trim();
                    }
                } catch (NumberFormatException e) {
                    // 忽略参数计数错误
                }

                JanusValue result = explain();
                ling = savedLing;
                return result;
            } else {
                return v;
            }
        }
    }

    static void initTokens() {
        tokens.put("func", s -> new JanusValue());
        tokens.put("print", s -> {
            JanusValue t = new JanusValue();
            StringBuilder line = new StringBuilder(s);
            while (line.length() > 0) {
                t = pexpl(line);
                switch (t.ty) {
                    case S: System.out.print(t.str); break;
                    case C: System.out.print(t._char); break;
                    case I: System.out.print(t._int); break;
                    case B: System.out.print(t._bool); break;
                    case V: System.out.print("()"); break;
                }
            }
            return t;
        });

        tokens.put("Debug", s -> new JanusValue());

        tokens.put("let", s -> {
            String[] parts = s.split("\\s+", 2);
            if (parts.length < 2) return new JanusValue();

            String varName = parts[0];
            StringBuilder valueStr = new StringBuilder(parts[1].trim());

            // 处理指针变量
            while (varName.startsWith("[")) {
                varName = varName.substring(1);
                StringBuilder temp = new StringBuilder(varName);
                JanusValue t = pexpl(temp);
                if (t.ty == Type.S) varName = t.str;
            }

            JanusValue value = pexpl(valueStr);
            vmap.put(varName, value);
            return value;
        });

        tokens.put("+", s -> {
            StringBuilder line = new StringBuilder(s);
            JanusValue t1 = pexpl(line);
            JanusValue t2 = pexpl(line);

            if (t1.ty == Type.S || t2.ty == Type.S) {
                JanusValue r = new JanusValue(Type.S);
                r.str = t1.toString() + t2.toString();
                return r;
            } else {
                JanusValue r = new JanusValue(Type.I);
                r._int = t1._int + t2._int;
                return r;
            }
        });

        tokens.put("-", s -> {
            StringBuilder line = new StringBuilder(s);
            JanusValue t1 = pexpl(line);
            JanusValue t2 = pexpl(line);
            JanusValue r = new JanusValue(Type.I);
            r._int = t1._int - t2._int;
            return r;
        });

        tokens.put("*", s -> {
            StringBuilder line = new StringBuilder(s);
            JanusValue t1 = pexpl(line);
            JanusValue t2 = pexpl(line);
            JanusValue r = new JanusValue(Type.I);
            r._int = t1._int * t2._int;
            return r;
        });

        tokens.put("/", s -> {
            StringBuilder line = new StringBuilder(s);
            JanusValue t1 = pexpl(line);
            JanusValue t2 = pexpl(line);
            JanusValue r = new JanusValue(Type.I);
            if (t2._int != 0) {
                r._int = t1._int / t2._int;
            } else {
                System.err.println("Division by zero");
            }
            return r;
        });

        tokens.put("if", s -> {
            StringBuilder line = new StringBuilder(s);
            JanusValue t = pexpl(line);
            boolean condition = false;

            switch (t.ty) {
                case S: condition = !t.str.isEmpty(); break;
                case I: condition = t._int != 0; break;
                case B: condition = t._bool; break;
                case C: condition = t._char != 0; break;
            }

            if (!condition) {
                int blockCount = 1;
                while (blockCount > 0) {
                    ling++;
                    if (ling >= ops.size()) break;
                    String op = ops.get(ling);
                    if (op.startsWith("if") || op.startsWith("while")) blockCount++;
                    if (op.equals("}")) blockCount--;
                }
            }
            return new JanusValue();
        });

        tokens.put("}", s -> new JanusValue());

        tokens.put("while", s -> {
            StringBuilder line = new StringBuilder(s);
            int startLine = ling;

            while (true) {
                ling = startLine;
                StringBuilder cond = new StringBuilder(line.toString());
                JanusValue c = pexpl(cond);

                boolean condition = false;
                switch (c.ty) {
                    case S: condition = !c.str.isEmpty(); break;
                    case I: condition = c._int != 0; break;
                    case B: condition = c._bool; break;
                }

                if (!condition) break;

                int blockCount = 1;
                int current = startLine + 1;
                while (current < ops.size() && blockCount > 0) {
                    String op = ops.get(current);
                    if (op.equals("}")) blockCount--;
                    else if (op.contains("{")) blockCount++;

                    ling = current;
                    pexpl(new StringBuilder(op));
                    current++;
                }
            }

            // 跳过循环体
            int blockCount = 1;
            while (blockCount > 0) {
                ling++;
                if (ling >= ops.size()) break;
                String op = ops.get(ling);
                if (op.startsWith("if") || op.startsWith("while")) blockCount++;
                if (op.equals("}")) blockCount--;
            }
            return new JanusValue();
        });

        tokens.put("true", s -> {
            JanusValue t = new JanusValue(Type.B);
            t._bool = true;
            return t;
        });

        tokens.put("false", s -> {
            JanusValue t = new JanusValue(Type.B);
            t._bool = false;
            return t;
        });

        tokens.put(">", s -> {
            StringBuilder line = new StringBuilder(s);
            JanusValue t1 = pexpl(line);
            JanusValue t2 = pexpl(line);
            JanusValue r = new JanusValue(Type.B);
            r._bool = t1._int > t2._int;
            return r;
        });

        tokens.put("<", s -> {
            StringBuilder line = new StringBuilder(s);
            JanusValue t1 = pexpl(line);
            JanusValue t2 = pexpl(line);
            JanusValue r = new JanusValue(Type.B);
            r._bool = t1._int < t2._int;
            return r;
        });

        tokens.put(">=", s -> {
            StringBuilder line = new StringBuilder(s);
            JanusValue t1 = pexpl(line);
            JanusValue t2 = pexpl(line);
            JanusValue r = new JanusValue(Type.B);
            r._bool = t1._int >= t2._int;
            return r;
        });

        tokens.put("<=", s -> {
            StringBuilder line = new StringBuilder(s);
            JanusValue t1 = pexpl(line);
            JanusValue t2 = pexpl(line);
            JanusValue r = new JanusValue(Type.B);
            r._bool = t1._int <= t2._int;
            return r;
        });

        tokens.put("!", s -> {
            StringBuilder line = new StringBuilder(s);
            JanusValue t = pexpl(line);
            JanusValue r = new JanusValue(Type.B);
            r._bool = !(t._int != 0);
            return r;
        });

        tokens.put("&&", s -> {
            StringBuilder line = new StringBuilder(s);
            JanusValue t1 = pexpl(line);
            JanusValue t2 = pexpl(line);
            JanusValue r = new JanusValue(Type.B);
            r._bool = (t1._int != 0) && (t2._int != 0);
            return r;
        });

        tokens.put("||", s -> {
            StringBuilder line = new StringBuilder(s);
            JanusValue t1 = pexpl(line);
            JanusValue t2 = pexpl(line);
            JanusValue r = new JanusValue(Type.B);
            r._bool = (t1._int != 0) || (t2._int != 0);
            return r;
        });

        tokens.put("ret", s -> {
            StringBuilder line = new StringBuilder(s);
            ret = pexpl(line);
            throw new ReturnException();
        });

        tokens.put("scan", s -> {
            Scanner scan = new Scanner(System.in);
            String[] vars = s.split("\\s+");
            for (String var : vars) {
                if (var.isEmpty()) continue;

                String varName = var;
                while (varName.startsWith("[")) {
                    varName = varName.substring(1);
                    StringBuilder temp = new StringBuilder(varName);
                    JanusValue t = pexpl(temp);
                    if (t.ty == Type.S) varName = t.str;
                }

                if (vmap.containsKey(varName)) {
                    JanusValue v = vmap.get(varName);
                    switch (v.ty) {
                        case S: v.str = scan.next(); break;
                        case I: v._int = scan.nextInt(); break;
                        case C: v._char = scan.next().charAt(0); break;
                        case B: v._bool = scan.nextBoolean(); break;
                    }
                } else {
                    System.err.println("Undefined variable: " + varName);
                }
            }
            return new JanusValue();
        });

        tokens.put("input", s -> {
            Scanner scan = new Scanner(System.in);
            JanusValue t = new JanusValue(Type.S);
            t.str = scan.nextLine();
            return t;
        });

        tokens.put("srand", s -> {
            StringBuilder line = new StringBuilder(s);
            JanusValue sd = pexpl(line);
            if (sd.ty == Type.I) {
                janus_seed = sd._int;
            } else {
                System.err.println("'srand' requires integer seed");
            }
            return new JanusValue();
        });

        tokens.put("rand", s -> {
            janus_seed = janus_seed * 214013 + 2531011;
            JanusValue r = new JanusValue(Type.I);
            r._int = (janus_seed >> 16) & 0x7FFF;
            return r;
        });

        tokens.put("WIN32", s -> {
            JanusValue t = new JanusValue(Type.B);
            t._bool = System.getProperty("os.name").toLowerCase().contains("win");
            return t;
        });

        tokens.put("()", s -> new JanusValue(Type.V));
    }
}